library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union

Accuracy

Importing data: USIDNET

library(dplyr)
USIDNET_reducida_04a<-readRDS(paste0("data/","USIDNET_reducida_04a",".rds"))
USIDNET_reducida_04a

Train / Test Split

# lista_data_acc<-list()
USIDNET_reducida_05<-as.data.frame(USIDNET_reducida_04a)
USIDNET_reducida_05$dx<-as.numeric(as.factor(USIDNET_reducida_05$Category))
library(caret)
prop_extraer_base<-.75

set.seed(prop_extraer_base*100)
inTrain <- createDataPartition(y = USIDNET_reducida_05$dx,
                                     ## the outcome data are needed
                                     p = prop_extraer_base,#prop_para_partition_malos,
                                     ## The percentage of data in the
                                     ## training set
                                     list = FALSE)
pob00_train0 <- USIDNET_reducida_05[ inTrain,]
pob00_test0 <- USIDNET_reducida_05[ -inTrain,]
pob00_train0%>%as_data_frame(); pob00_test0%>%as_data_frame()
#names(pob00_train0)

Prep data for training

source("utils/descenso_gradiente_bbva.R")
x_ent <- pob00_train0 %>% 
  select( -id_px, -Category,-dx
  #   one_of(
  #     unique(subgb02_data01$VARIABLE_NUEVA_CLASIFICACION1[subgb02_data01$VARIABLE_NUEVA_CLASIFICACION1%in%names(USIDNET_reducida_01)]))
  # )%>%
  ) %>%
  as.matrix
y_ent <- pob00_train0$dx
x_ent_s <- scale(x_ent)
medias <- attr(x_ent_s, 'scaled:center')
sd <- attr(x_ent_s, 'scaled:scale')

x_ent%>%as_data_frame();x_ent_s%>%as_data_frame()

Prep data test

x_pr <- pob00_test0  %>% 
  select( -id_px, -Category,-dx
  #   one_of(
  #     unique(subgb02_data01$VARIABLE_NUEVA_CLASIFICACION1[subgb02_data01$VARIABLE_NUEVA_CLASIFICACION1%in%names(USIDNET_reducida_01)]))
  # )%>%
  ) %>%
  as.matrix
y_pr <- pob00_test0$dx

x_pr%>%as_data_frame()

Hyperparameters & Iterations

p<-ncol(x_ent)
K<-length(unique(USIDNET_reducida_05$dx))
# dev_ent <- devianza_calc(x = x_ent_s,y =  y_ent)
# grad <- grad_calc(x_ent = x_ent_s, y_ent)

dev_ent <- devianza_calc(x = x_ent_s,y =  y_ent)
grad <- grad_calc(x_ent = x_ent_s, y_ent)

iteraciones05 <- descenso(5001,rep(0, (p+1)*(K-1)), eta=0.0001, 
                          h_deriv = grad, dev_fun = dev_ent)
[1] "iteration:100 - betas from 101"
[1] "iteration:200 - betas from 201"
[1] "iteration:300 - betas from 301"
[1] "iteration:400 - betas from 401"
[1] "iteration:500 - betas from 501"
[1] "iteration:600 - betas from 601"
[1] "iteration:700 - betas from 701"
[1] "iteration:800 - betas from 801"
[1] "iteration:900 - betas from 901"
[1] "iteration:1000 - betas from 1001"
[1] "iteration:1100 - betas from 1101"
[1] "iteration:1200 - betas from 1201"
[1] "iteration:1300 - betas from 1301"
[1] "iteration:1400 - betas from 1401"
[1] "iteration:1500 - betas from 1501"
[1] "iteration:1600 - betas from 1601"
[1] "iteration:1700 - betas from 1701"
[1] "iteration:1800 - betas from 1801"
[1] "iteration:1900 - betas from 1901"
[1] "iteration:2000 - betas from 2001"
[1] "iteration:2100 - betas from 2101"
[1] "iteration:2200 - betas from 2201"
[1] "iteration:2300 - betas from 2301"
[1] "iteration:2400 - betas from 2401"
[1] "iteration:2500 - betas from 2501"
[1] "iteration:2600 - betas from 2601"
[1] "iteration:2700 - betas from 2701"
[1] "iteration:2800 - betas from 2801"
[1] "iteration:2900 - betas from 2901"
[1] "iteration:3000 - betas from 3001"
[1] "iteration:3100 - betas from 3101"
[1] "iteration:3200 - betas from 3201"
[1] "iteration:3300 - betas from 3301"
[1] "iteration:3400 - betas from 3401"
[1] "iteration:3500 - betas from 3501"
[1] "iteration:3600 - betas from 3601"
[1] "iteration:3700 - betas from 3701"
[1] "iteration:3800 - betas from 3801"
[1] "iteration:3900 - betas from 3901"
[1] "iteration:4000 - betas from 4001"
[1] "iteration:4100 - betas from 4101"
[1] "iteration:4200 - betas from 4201"
[1] "iteration:4300 - betas from 4301"
[1] "iteration:4400 - betas from 4401"
[1] "iteration:4500 - betas from 4501"
[1] "iteration:4600 - betas from 4601"
[1] "iteration:4700 - betas from 4701"
[1] "iteration:4800 - betas from 4801"
[1] "iteration:4900 - betas from 4901"
[1] "iteration:5000 - betas from 5001"
iteraciones<-iteraciones05
devianzas_iteraciones<-sapply(1:nrow(iteraciones),function(i) dev_ent(iteraciones[i,]))
df_devianzas_iteraciones<-data.frame(
  id=1:nrow(iteraciones),
  devianzas=devianzas_iteraciones
)

saveRDS(iteraciones,"data/iteraciones_5001_0s_0.0001_copy.rds")

All the deviances

# iteraciones<-readRDS("data/iteraciones_5001_0s_0.0001_copy.rds")
p<-ncol(x_ent)
K<-length(unique(USIDNET_reducida_05$dx))
dev_ent <- devianza_calc(x = x_ent_s,y =  y_ent)
grad <- grad_calc(x_ent = x_ent_s, y_ent)

devianzas_iteraciones<-sapply(1:nrow(iteraciones),function(i) dev_ent(iteraciones[i,]))
df_devianzas_iteraciones<-data.frame(
  id=1:nrow(iteraciones),
  deviances=devianzas_iteraciones
)
df_devianzas_iteraciones

Top5 Performances

lista_data_acc<-list()

top5<-head(df_devianzas_iteraciones%>%arrange(deviances))
data_acc<-data_frame()
for(id_top in 1: nrow(top5)){
  id_mindev<-top5[id_top,1]
  print(paste0("-------->>>> id: ",id_top,"<<<<--------"))
  print(id_mindev)
  
  print("deviance:")
  print(dev_ent(iteraciones[id_mindev,]))
  
  probas <- pred_multinom(x_ent_s, iteraciones[id_mindev,])
  clase <- apply(probas, 1, which.max)
  print("train:")
  #print(table(clase, y_ent ))
  acc_train<-1 - mean(clase != y_ent)
  print(acc_train)
  
  x_pr_s <- scale(x_pr, center = medias, scale = sd)
  probas <- pred_multinom(x_pr_s, iteraciones[id_mindev,])
  clase <- apply(probas, 1, which.max)
  print("test:")
  #print(table(clase, y_pr ))
  acc_test<-1 - mean(clase != y_pr)
  print(acc_test)
  
  data_acc<-data_acc%>%
    bind_rows(
      data_frame(
        id=id_mindev,
        dev_train=dev_ent(iteraciones[id_mindev,]),
        acc_train=acc_train,
        acc_test=acc_test 
      )
    )
}
[1] "-------->>>> id: 1<<<<--------"
[1] 4840
[1] "deviance:"
[1] 4298.162
[1] "train:"
[1] 0.6562848
[1] "test:"
[1] 0.5401338
[1] "-------->>>> id: 2<<<<--------"
[1] 4839
[1] "deviance:"
[1] 4378.981
[1] "train:"
[1] 0.6779755
[1] "test:"
[1] 0.5618729
[1] "-------->>>> id: 3<<<<--------"
[1] 4273
[1] "deviance:"
[1] 4428.902
[1] "train:"
[1] 0.6451613
[1] "test:"
[1] 0.5183946
[1] "-------->>>> id: 4<<<<--------"
[1] 4272
[1] "deviance:"
[1] 4488.603
[1] "train:"
[1] 0.6457175
[1] "test:"
[1] 0.5284281
[1] "-------->>>> id: 5<<<<--------"
[1] 4614
[1] "deviance:"
[1] 4503.951
[1] "train:"
[1] 0.6490545
[1] "test:"
[1] 0.5401338
[1] "-------->>>> id: 6<<<<--------"
[1] 4810
[1] "deviance:"
[1] 4521.62
[1] "train:"
[1] 0.6529477
[1] "test:"
[1] 0.5334448
# idmin<-data_acc%>%
#   filter(acc_test==max(acc_test))%>%
#   filter(acc_train==max(acc_train))%>%
#   filter(id==min(id))%>%
#   pull(id)
idmin<-data_acc%>%
  filter(dev_train==min(dev_train))%>%
  filter(acc_test==max(acc_test))%>%
  filter(acc_train==max(acc_train))%>%
  filter(id==min(id))%>%
  pull(id)


print("--------------------------------------------")
[1] "--------------------------------------------"
print("--------------- BEST  RESULT --------------")
[1] "--------------- BEST  RESULT --------------"
print("-------------- A C C U R A C Y --------------")
[1] "-------------- A C C U R A C Y --------------"
probas <- pred_multinom(x_ent_s, iteraciones[idmin,])
clase <- apply(probas, 1, which.max)
print("train:")
[1] "train:"
table_train<-table(clase, y_ent)
accuracy_train<-1 - mean(clase != y_ent)
print(accuracy_train)
[1] 0.6562848
print("
...
      ")
[1] "\n...\n      "
x_pr_s <- scale(x_pr, center = medias, scale = sd)
probas <- pred_multinom(x_pr_s, iteraciones[idmin,])
clase <- apply(probas, 1, which.max)
print("test:")
[1] "test:"
table_test<-table(clase, y_pr )
accuracy_test<-1 - mean(clase != y_pr)
print(accuracy_test)
[1] 0.5401338
mean(table_test)
[1] 4.152778
# lista_data_acc[[paste0("init",length(lista_data_acc))]]<-list(
lista_data_acc[["usidnet4a"]]<-list(
  "data_acc"=data_acc,
  "idmin"=idmin,
  "table_train"=table_train,
  "table_test"=table_test,
  "accuracy_train"=accuracy_train,
  "accuracy_test"=accuracy_test
)


print("
...
      ")
[1] "\n...\n      "
print("--------------------------------------------")
[1] "--------------------------------------------"
print("-------------- CONFUSION MATRIX -------------")
[1] "-------------- CONFUSION MATRIX -------------"
print("train:")
[1] "train:"
lista_data_acc$usidnet4a$table_train;
     y_ent
clase   1   2   3   4   5   6   7   8   9  10  11  12
   1   23   4   0   0   2  26   5   1   0   0   2   1
   2    3  48   1   0   2  26   5   1   0   0   5   1
   3    4   1  95   0   5  19   2   0   0   0   4   2
   4    0   0   0   5   0   2   0   0   0   0   1   1
   5    2   2   9   0  68  25   5   0   0   1  10   1
   6   48  23   7   4  17 581  31  14   0   3  31   8
   7    4  13   5   0   5  97 251   4   0   0  29  13
   8    2   2   1   0   0  10   1  12   0   0   1   0
   9    0   0   0   0   0   0   0   0   3   0   0   0
   10   1   1   0   0   2   0   0   0   0  15   3   0
   11   0   1   0   1   0  11   2   1   0   0  58   2
   12   1   3   1   0   4  19   3   0   0   0   8  21
print("test:")
[1] "test:"
lista_data_acc$usidnet4a$table_test;
     y_pr
clase   1   2   3   4   5   6   7   8   9  10  11  12
   1    4   2   1   0   0   8   1   0   1   0   5   0
   2    5  12   1   0   0  16   3   0   0   0   1   0
   3    0   3  19   0   6   4   0   0   1   0   5   0
   4    0   0   0   0   0   0   0   1   0   0   0   0
   5    1   0   0   0  11  11   1   1   0   1   1   0
   6   14  13   7   2   3 183  12   4   1   3  17   6
   7    1   4   5   0   2  38  81   3   0   0   6   6
   8    1   0   0   0   0   8   0   1   0   1   3   0
   9    0   0   0   0   0   2   0   0   0   0   0   0
   10   0   1   2   0   3   1   0   1   0   1   0   0
   11   3   1   0   0   0   5   2   2   0   0  10   0
   12   0   1   0   0   2   6   1   0   0   0   2   1
saveRDS(lista_data_acc,"data/lista_data_acc_usidnet4a_copy.rds")

Importances / Weights for feature

Taking the betas from minimum deviance

data_acc<-lista_data_acc$usidnet4a$data_acc
# idmin<-data_acc%>%
#   filter(acc_test==max(acc_test))%>%
#   filter(acc_train==max(acc_train))%>%
#   filter(id==min(id))%>%
#   pull(id)
idmin<-data_acc%>%
  filter(dev_train==min(dev_train))%>%
  filter(id==min(id))%>%
  pull(id)
betas<-iteraciones[idmin,]
# betas
#p;K;
#(p+1)*(K-1);
#length(betas)


df_betas <- as_data_frame(matrix(betas, K - 1, p + 1 , byrow = TRUE))%>%
  bind_rows(
    as_data_frame(matrix(c(1,rep(0,p)),nrow=1))
    )%>%
  mutate(
    dx=as.character(row_number())
  )%>%
  left_join(
    USIDNET_reducida_05%>%
      group_by(dx,Category)%>%
      summarise(
        n=n()
      )%>%
      ungroup()%>%
      mutate(
        dx=as.character(dx),
        prop=round(n/sum(n),3)
      )
  )%>%
  select(dx,Category,n,prop,one_of(names(.)))
Joining, by = "dx"
  
names(df_betas)<-c("dx","Category","n","prop",paste0("beta_",(seq(p+1)-1)))
df_betas
# prod_matrices<-as.matrix(cbind(1, x)) %*% t(beta_mat)

Calculating weights for feature per Dx

excluir<-c("dx","Category","n","prop")
    ptsig00<-as.data.frame(
      pob00_train0%>% 
        # select( -id_px, -Category, -dx)%>%
        select( -id_px, -Category)%>%
        mutate(intercept=1)%>%
        select(dx,intercept, one_of(names(.)))
  )#[c("malos","denomsy",names(siestan)[!names(siestan)%in%excluir])]

lista_resultados<-list()
tbl_pesos<-data_frame()
for(clase_i in 1:K){
  # clase_i<-1
  print(clase_i)
  dx_tmp<-pob00_train0%>%filter(dx==clase_i)%>%distinct(Category)%>%pull(Category)
  n_tmp<-df_betas%>%filter(dx==clase_i)%>%distinct(n)%>%pull(n)
  prop_tmp<-df_betas%>%filter(dx==clase_i)%>%distinct(prop)%>%pull(prop)
  print(dx_tmp)
  siestan<-as.data.frame(df_betas)[clase_i,]
  # ptsig00<-pob_test[c("malos","denomsy",names(siestan)[!names(siestan)%in%excluir])]
  #   ptsig<-as.data.frame(apply(ptsig00,2,as.numeric))
    ptsig<-as.data.frame(apply(ptsig00,2,as.numeric))
    varTi<-0
    # data_vars<-as.data.frame(ptsig[1,names(siestan)[!names(siestan)%in%excluir][-1]])
    data_vars<-as.data.frame(ptsig[1,names(ptsig00)[-c(1:2)]])
    names(data_vars)<-names(ptsig00)[-c(1:2)]
    data_vars[1,]<-0
    data_vars[2,]<-0
    # data_vars[3,]<-0
    
    for(j in 1:(length(names(ptsig))-2)){
      # j<-1
      #print(names(ptsig)[j+2])
      #print(names(siestan[!names(siestan)%in%excluir][1+j]))
      betawoe<-ptsig[,2+j]*as.numeric(siestan[!names(siestan)%in%excluir][1+j])
      ptsig<-cbind(ptsig,betawoe)
      
      varj<-round(sd(ptsig$betawoe),4)
      
      # woe_var<-names(siestan)[!names(siestan)%in%excluir][j+1]
      # var_original<-gsub("woe_","",woe_var)
      # mtr3_tmp<-MTR3_yk(ptsig[,3+j],ptsig$malos,ptsig$denomsy)
      
      # gini<-as.data.frame(
      #   df_ginis%>%
      #     filter(var==woe_var)
      # )$Gini #unique(lmtr5$lista$woes_nesp$Gini[lmtr5$lista$woes_nesp$var==var_tmp])
      # mtr4_tmp<-MTR4_yk(ptsig,"woe_IM_MEDIO_PAGO_TDC_6M",vobj = "malos",denomsy = ptsig$denomsy)
      
      data_vars[1,names(ptsig)[j+2]]<-names(siestan[!names(siestan)%in%excluir][1+j])
      data_vars[2,names(ptsig)[j+2]]<-varj
      # data_vars[3,names(ptsig)[j+2]]<-names(ptsig)[j+2]
      # data_vars[3,names(siestan)[!names(siestan)%in%excluir][1+j]]<-gini #max(mtr3_tmp$ga,mtr3_tmp$gd)
      
      varTi<-varTi + varj
      
      names(ptsig)[ncol(ptsig)]<-paste("beta",gsub(" ","_",names(ptsig)[2+j]),sep="_")
      
    }
    data_vars$varTi<-varTi
    # data_vars$varTi[3]<-0
    pesos<-round(as.numeric(data_vars[2,])/data_vars$varTi[2],4)
    data_vars<-rbind(data_vars,pesos)
    row.names(data_vars)<-c("id_beta","desviacion_s","pesos")#"nombre_original","pesos")
    #print(data_vars)
    data_vars<-as.data.frame(t(data_vars))
    #print(data_vars)
    
    #print(length(t(siestan[!names(siestan)%in%excluir][-1])))
    #blabla<-t(siestan[!names(siestan)%in%excluir][-1])
    #print(blabla)
    data_vars$beta_value <- c(t(siestan[!names(siestan)%in%excluir][-1]),-9999)
    data_vars$clase_i<-clase_i
    data_vars$Diagnostico<-dx_tmp
    data_vars$num_casos_dx<-n_tmp
    data_vars$proporcion_casos_dx<-prop_tmp
    data_vars$variable<-row.names(data_vars)
    
    # data_vars<-data_vars[,c(6,1:5)]
    data_vars<-data_vars%>%
      select(Diagnostico,clase_i,num_casos_dx,proporcion_casos_dx,variable,id_beta,beta_value,desviacion_s,pesos)
    
    
    tbl_pesos<-rbind(tbl_pesos,data_vars)
}
[1] 1
[1] "AB Deficiency"
[1] 2
[1] "AGAMMA"
[1] 3
[1] "CGD"
[1] 4
[1] "COMPDEF"
[1] 5
[1] "CORE"
[1] 6
[1] "CVID"
[1] 7
[1] "DGS"
[1] 8
[1] "HIGM"
[1] 9
[1] "LAD"
[1] 10
[1] "NEMO"
[1] 11
[1] "SCID"
[1] 12
[1] "WAS"
tbl_pesos<-tbl_pesos%>%
  mutate(
    variable=gsub('Bajo','low',variable),
    variable=gsub('Alto','high',variable),
    variable=gsub('LINFOCITOS','Lymphocytes',variable),
    variable=gsub('LEUCOCITOS','Leukocytes',variable),
    variable=gsub('MONOCITOS','Monocytes',variable),
    variable=gsub('NEUTROFILOS','Neutrophils',variable),
    variable=gsub('PLAQUETAS','Plateletes',variable),
    variable=gsub('EOSINOFILOS','Eosinophils',variable),
    variable=gsub('Dolor_abdominal','I_Abdominal_pain',variable),
    variable=gsub('Reflujo_gastroesofagico','I_Gastroesophageal_reflux',variable),
    variable=gsub('Dolor_toracico','I_Thoracic_pain',variable),
    variable=gsub('Dolor','I_Pain',variable),
    variable=gsub('Alto','high',variable)
  )
tbl_pesos%>%
  select(Diagnostico,num_casos_dx,proporcion_casos_dx,variable,beta_value,pesos)%>%
  rename(Dx = Diagnostico)%>%
  rename(number_cases_per_dx = num_casos_dx)%>%
  rename(percentage_cases_per_dx = proporcion_casos_dx)%>%
  rename(feature = variable)%>%
  rename(weight_importance = pesos)

Top 5 more important features per DX

tbl_pesos_01%>%
  group_by(Diagnostico)%>%
  arrange(Diagnostico,desc(pesos))%>%
  mutate(
    nivel_pesos=row_number()
  )%>%
  filter(nivel_pesos<=5)%>%
  ungroup()%>%
  arrange(desc(proporcion_casos_dx),Diagnostico,desc(pesos))%>%
  select(Diagnostico,variable,beta_value,pesos)%>%
  rename(Dx = Diagnostico)%>%
  rename(feature = variable)%>%
  rename(weight_importance = pesos)

Final Comments

LS0tCnRpdGxlOiAiTXVsdGlub21pYWwgbG9naXN0aWMgcmVncmVzc2lvbiIKc3VidGl0bGU6ICJUaHJvdWdoIGdyYWRpZW50IGRlc2NlbnQiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KYGBge3J9CmxpYnJhcnkoZHBseXIpCmBgYAoKIyBBY2N1cmFjeSB7LnRhYnNldH0KCiMjIEltcG9ydGluZyBkYXRhOiBVU0lETkVUCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpVU0lETkVUX3JlZHVjaWRhXzA0YTwtcmVhZFJEUyhwYXN0ZTAoImRhdGEvIiwiVVNJRE5FVF9yZWR1Y2lkYV8wNGEiLCIucmRzIikpClVTSURORVRfcmVkdWNpZGFfMDRhCmBgYAoKIyMgKipUcmFpbiAvIFRlc3QgU3BsaXQqKgoKYGBge3J9CiMgbGlzdGFfZGF0YV9hY2M8LWxpc3QoKQpVU0lETkVUX3JlZHVjaWRhXzA1PC1hcy5kYXRhLmZyYW1lKFVTSURORVRfcmVkdWNpZGFfMDRhKQpVU0lETkVUX3JlZHVjaWRhXzA1JGR4PC1hcy5udW1lcmljKGFzLmZhY3RvcihVU0lETkVUX3JlZHVjaWRhXzA1JENhdGVnb3J5KSkKbGlicmFyeShjYXJldCkKcHJvcF9leHRyYWVyX2Jhc2U8LS43NQoKc2V0LnNlZWQocHJvcF9leHRyYWVyX2Jhc2UqMTAwKQppblRyYWluIDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oeSA9IFVTSURORVRfcmVkdWNpZGFfMDUkZHgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIyB0aGUgb3V0Y29tZSBkYXRhIGFyZSBuZWVkZWQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHAgPSBwcm9wX2V4dHJhZXJfYmFzZSwjcHJvcF9wYXJhX3BhcnRpdGlvbl9tYWxvcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMjIFRoZSBwZXJjZW50YWdlIG9mIGRhdGEgaW4gdGhlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIyB0cmFpbmluZyBzZXQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QgPSBGQUxTRSkKcG9iMDBfdHJhaW4wIDwtIFVTSURORVRfcmVkdWNpZGFfMDVbIGluVHJhaW4sXQpwb2IwMF90ZXN0MCA8LSBVU0lETkVUX3JlZHVjaWRhXzA1WyAtaW5UcmFpbixdCnBvYjAwX3RyYWluMCU+JWFzX2RhdGFfZnJhbWUoKTsgcG9iMDBfdGVzdDAlPiVhc19kYXRhX2ZyYW1lKCkKI25hbWVzKHBvYjAwX3RyYWluMCkKYGBgCgojIyAqKlByZXAgZGF0YSBmb3IgdHJhaW5pbmcqKgpgYGB7cn0Kc291cmNlKCJ1dGlscy9kZXNjZW5zb19ncmFkaWVudGVfYmJ2YS5SIikKeF9lbnQgPC0gcG9iMDBfdHJhaW4wICU+JSAKICBzZWxlY3QoIC1pZF9weCwgLUNhdGVnb3J5LC1keAogICMgICBvbmVfb2YoCiAgIyAgICAgdW5pcXVlKHN1YmdiMDJfZGF0YTAxJFZBUklBQkxFX05VRVZBX0NMQVNJRklDQUNJT04xW3N1YmdiMDJfZGF0YTAxJFZBUklBQkxFX05VRVZBX0NMQVNJRklDQUNJT04xJWluJW5hbWVzKFVTSURORVRfcmVkdWNpZGFfMDEpXSkpCiAgIyApJT4lCiAgKSAlPiUKICBhcy5tYXRyaXgKeV9lbnQgPC0gcG9iMDBfdHJhaW4wJGR4CnhfZW50X3MgPC0gc2NhbGUoeF9lbnQpCm1lZGlhcyA8LSBhdHRyKHhfZW50X3MsICdzY2FsZWQ6Y2VudGVyJykKc2QgPC0gYXR0cih4X2VudF9zLCAnc2NhbGVkOnNjYWxlJykKCnhfZW50JT4lYXNfZGF0YV9mcmFtZSgpO3hfZW50X3MlPiVhc19kYXRhX2ZyYW1lKCkKYGBgCgojIyAqKlByZXAgZGF0YSB0ZXN0KioKYGBge3J9CnhfcHIgPC0gcG9iMDBfdGVzdDAgICU+JSAKICBzZWxlY3QoIC1pZF9weCwgLUNhdGVnb3J5LC1keAogICMgICBvbmVfb2YoCiAgIyAgICAgdW5pcXVlKHN1YmdiMDJfZGF0YTAxJFZBUklBQkxFX05VRVZBX0NMQVNJRklDQUNJT04xW3N1YmdiMDJfZGF0YTAxJFZBUklBQkxFX05VRVZBX0NMQVNJRklDQUNJT04xJWluJW5hbWVzKFVTSURORVRfcmVkdWNpZGFfMDEpXSkpCiAgIyApJT4lCiAgKSAlPiUKICBhcy5tYXRyaXgKeV9wciA8LSBwb2IwMF90ZXN0MCRkeAoKeF9wciU+JWFzX2RhdGFfZnJhbWUoKQpgYGAKCiMjICoqSHlwZXJwYXJhbWV0ZXJzICYgSXRlcmF0aW9ucyoqCmBgYHtyfQpwPC1uY29sKHhfZW50KQpLPC1sZW5ndGgodW5pcXVlKFVTSURORVRfcmVkdWNpZGFfMDUkZHgpKQojIGRldl9lbnQgPC0gZGV2aWFuemFfY2FsYyh4ID0geF9lbnRfcyx5ID0gIHlfZW50KQojIGdyYWQgPC0gZ3JhZF9jYWxjKHhfZW50ID0geF9lbnRfcywgeV9lbnQpCgpkZXZfZW50IDwtIGRldmlhbnphX2NhbGMoeCA9IHhfZW50X3MseSA9ICB5X2VudCkKZ3JhZCA8LSBncmFkX2NhbGMoeF9lbnQgPSB4X2VudF9zLCB5X2VudCkKCml0ZXJhY2lvbmVzMDUgPC0gZGVzY2Vuc28oNTAwMSxyZXAoMCwgKHArMSkqKEstMSkpLCBldGE9MC4wMDAxLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBoX2Rlcml2ID0gZ3JhZCwgZGV2X2Z1biA9IGRldl9lbnQpCml0ZXJhY2lvbmVzPC1pdGVyYWNpb25lczA1CmRldmlhbnphc19pdGVyYWNpb25lczwtc2FwcGx5KDE6bnJvdyhpdGVyYWNpb25lcyksZnVuY3Rpb24oaSkgZGV2X2VudChpdGVyYWNpb25lc1tpLF0pKQpkZl9kZXZpYW56YXNfaXRlcmFjaW9uZXM8LWRhdGEuZnJhbWUoCiAgaWQ9MTpucm93KGl0ZXJhY2lvbmVzKSwKICBkZXZpYW56YXM9ZGV2aWFuemFzX2l0ZXJhY2lvbmVzCikKCnNhdmVSRFMoaXRlcmFjaW9uZXMsImRhdGEvaXRlcmFjaW9uZXNfNTAwMV8wc18wLjAwMDFfY29weS5yZHMiKQpgYGAKCiMjICoqQWxsIHRoZSBkZXZpYW5jZXMqKgpgYGB7cn0KIyBpdGVyYWNpb25lczwtcmVhZFJEUygiZGF0YS9pdGVyYWNpb25lc181MDAxXzBzXzAuMDAwMV9jb3B5LnJkcyIpCnA8LW5jb2woeF9lbnQpCks8LWxlbmd0aCh1bmlxdWUoVVNJRE5FVF9yZWR1Y2lkYV8wNSRkeCkpCmRldl9lbnQgPC0gZGV2aWFuemFfY2FsYyh4ID0geF9lbnRfcyx5ID0gIHlfZW50KQpncmFkIDwtIGdyYWRfY2FsYyh4X2VudCA9IHhfZW50X3MsIHlfZW50KQoKZGV2aWFuemFzX2l0ZXJhY2lvbmVzPC1zYXBwbHkoMTpucm93KGl0ZXJhY2lvbmVzKSxmdW5jdGlvbihpKSBkZXZfZW50KGl0ZXJhY2lvbmVzW2ksXSkpCmRmX2Rldmlhbnphc19pdGVyYWNpb25lczwtZGF0YS5mcmFtZSgKICBpZD0xOm5yb3coaXRlcmFjaW9uZXMpLAogIGRldmlhbmNlcz1kZXZpYW56YXNfaXRlcmFjaW9uZXMKKQpkZl9kZXZpYW56YXNfaXRlcmFjaW9uZXMKYGBgCiMjICoqVG9wNSBQZXJmb3JtYW5jZXMqKgpgYGB7cn0KbGlzdGFfZGF0YV9hY2M8LWxpc3QoKQoKdG9wNTwtaGVhZChkZl9kZXZpYW56YXNfaXRlcmFjaW9uZXMlPiVhcnJhbmdlKGRldmlhbmNlcykpCmRhdGFfYWNjPC1kYXRhX2ZyYW1lKCkKZm9yKGlkX3RvcCBpbiAxOiBucm93KHRvcDUpKXsKICBpZF9taW5kZXY8LXRvcDVbaWRfdG9wLDFdCiAgcHJpbnQocGFzdGUwKCItLS0tLS0tLT4+Pj4gaWQ6ICIsaWRfdG9wLCI8PDw8LS0tLS0tLS0iKSkKICBwcmludChpZF9taW5kZXYpCiAgCiAgcHJpbnQoImRldmlhbmNlOiIpCiAgcHJpbnQoZGV2X2VudChpdGVyYWNpb25lc1tpZF9taW5kZXYsXSkpCiAgCiAgcHJvYmFzIDwtIHByZWRfbXVsdGlub20oeF9lbnRfcywgaXRlcmFjaW9uZXNbaWRfbWluZGV2LF0pCiAgY2xhc2UgPC0gYXBwbHkocHJvYmFzLCAxLCB3aGljaC5tYXgpCiAgcHJpbnQoInRyYWluOiIpCiAgI3ByaW50KHRhYmxlKGNsYXNlLCB5X2VudCApKQogIGFjY190cmFpbjwtMSAtIG1lYW4oY2xhc2UgIT0geV9lbnQpCiAgcHJpbnQoYWNjX3RyYWluKQogIAogIHhfcHJfcyA8LSBzY2FsZSh4X3ByLCBjZW50ZXIgPSBtZWRpYXMsIHNjYWxlID0gc2QpCiAgcHJvYmFzIDwtIHByZWRfbXVsdGlub20oeF9wcl9zLCBpdGVyYWNpb25lc1tpZF9taW5kZXYsXSkKICBjbGFzZSA8LSBhcHBseShwcm9iYXMsIDEsIHdoaWNoLm1heCkKICBwcmludCgidGVzdDoiKQogICNwcmludCh0YWJsZShjbGFzZSwgeV9wciApKQogIGFjY190ZXN0PC0xIC0gbWVhbihjbGFzZSAhPSB5X3ByKQogIHByaW50KGFjY190ZXN0KQogIAogIGRhdGFfYWNjPC1kYXRhX2FjYyU+JQogICAgYmluZF9yb3dzKAogICAgICBkYXRhX2ZyYW1lKAogICAgICAgIGlkPWlkX21pbmRldiwKICAgICAgICBkZXZfdHJhaW49ZGV2X2VudChpdGVyYWNpb25lc1tpZF9taW5kZXYsXSksCiAgICAgICAgYWNjX3RyYWluPWFjY190cmFpbiwKICAgICAgICBhY2NfdGVzdD1hY2NfdGVzdCAKICAgICAgKQogICAgKQp9CiMgaWRtaW48LWRhdGFfYWNjJT4lCiMgICBmaWx0ZXIoYWNjX3Rlc3Q9PW1heChhY2NfdGVzdCkpJT4lCiMgICBmaWx0ZXIoYWNjX3RyYWluPT1tYXgoYWNjX3RyYWluKSklPiUKIyAgIGZpbHRlcihpZD09bWluKGlkKSklPiUKIyAgIHB1bGwoaWQpCmlkbWluPC1kYXRhX2FjYyU+JQogIGZpbHRlcihkZXZfdHJhaW49PW1pbihkZXZfdHJhaW4pKSU+JQogIGZpbHRlcihhY2NfdGVzdD09bWF4KGFjY190ZXN0KSklPiUKICBmaWx0ZXIoYWNjX3RyYWluPT1tYXgoYWNjX3RyYWluKSklPiUKICBmaWx0ZXIoaWQ9PW1pbihpZCkpJT4lCiAgcHVsbChpZCkKCgpwcmludCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0iKQpwcmludCgiLS0tLS0tLS0tLS0tLS0tIEJFU1QgIFJFU1VMVCAtLS0tLS0tLS0tLS0tLSIpCnByaW50KCItLS0tLS0tLS0tLS0tLSBBIEMgQyBVIFIgQSBDIFkgLS0tLS0tLS0tLS0tLS0iKQpwcm9iYXMgPC0gcHJlZF9tdWx0aW5vbSh4X2VudF9zLCBpdGVyYWNpb25lc1tpZG1pbixdKQpjbGFzZSA8LSBhcHBseShwcm9iYXMsIDEsIHdoaWNoLm1heCkKcHJpbnQoInRyYWluOiIpCnRhYmxlX3RyYWluPC10YWJsZShjbGFzZSwgeV9lbnQpCmFjY3VyYWN5X3RyYWluPC0xIC0gbWVhbihjbGFzZSAhPSB5X2VudCkKcHJpbnQoYWNjdXJhY3lfdHJhaW4pCgpwcmludCgiCi4uLgogICAgICAiKQoKeF9wcl9zIDwtIHNjYWxlKHhfcHIsIGNlbnRlciA9IG1lZGlhcywgc2NhbGUgPSBzZCkKcHJvYmFzIDwtIHByZWRfbXVsdGlub20oeF9wcl9zLCBpdGVyYWNpb25lc1tpZG1pbixdKQpjbGFzZSA8LSBhcHBseShwcm9iYXMsIDEsIHdoaWNoLm1heCkKcHJpbnQoInRlc3Q6IikKdGFibGVfdGVzdDwtdGFibGUoY2xhc2UsIHlfcHIgKQphY2N1cmFjeV90ZXN0PC0xIC0gbWVhbihjbGFzZSAhPSB5X3ByKQpwcmludChhY2N1cmFjeV90ZXN0KQoKCm1lYW4odGFibGVfdGVzdCkKIyBsaXN0YV9kYXRhX2FjY1tbcGFzdGUwKCJpbml0IixsZW5ndGgobGlzdGFfZGF0YV9hY2MpKV1dPC1saXN0KApsaXN0YV9kYXRhX2FjY1tbInVzaWRuZXQ0YSJdXTwtbGlzdCgKICAiZGF0YV9hY2MiPWRhdGFfYWNjLAogICJpZG1pbiI9aWRtaW4sCiAgInRhYmxlX3RyYWluIj10YWJsZV90cmFpbiwKICAidGFibGVfdGVzdCI9dGFibGVfdGVzdCwKICAiYWNjdXJhY3lfdHJhaW4iPWFjY3VyYWN5X3RyYWluLAogICJhY2N1cmFjeV90ZXN0Ij1hY2N1cmFjeV90ZXN0CikKCgpwcmludCgiCi4uLgogICAgICAiKQoKcHJpbnQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIikKcHJpbnQoIi0tLS0tLS0tLS0tLS0tIENPTkZVU0lPTiBNQVRSSVggLS0tLS0tLS0tLS0tLSIpCgpwcmludCgidHJhaW46IikKbGlzdGFfZGF0YV9hY2MkdXNpZG5ldDRhJHRhYmxlX3RyYWluOwpwcmludCgidGVzdDoiKQpsaXN0YV9kYXRhX2FjYyR1c2lkbmV0NGEkdGFibGVfdGVzdDsKc2F2ZVJEUyhsaXN0YV9kYXRhX2FjYywiZGF0YS9saXN0YV9kYXRhX2FjY191c2lkbmV0NGFfY29weS5yZHMiKQpgYGAKCgojIEltcG9ydGFuY2VzIC8gV2VpZ2h0cyBmb3IgZmVhdHVyZSAgey50YWJzZXR9CgoKIyMgKipUYWtpbmcgdGhlIGJldGFzIGZyb20gbWluaW11bSBkZXZpYW5jZSoqCmBgYHtyfQpkYXRhX2FjYzwtbGlzdGFfZGF0YV9hY2MkdXNpZG5ldDRhJGRhdGFfYWNjCiMgaWRtaW48LWRhdGFfYWNjJT4lCiMgICBmaWx0ZXIoYWNjX3Rlc3Q9PW1heChhY2NfdGVzdCkpJT4lCiMgICBmaWx0ZXIoYWNjX3RyYWluPT1tYXgoYWNjX3RyYWluKSklPiUKIyAgIGZpbHRlcihpZD09bWluKGlkKSklPiUKIyAgIHB1bGwoaWQpCmlkbWluPC1kYXRhX2FjYyU+JQogIGZpbHRlcihkZXZfdHJhaW49PW1pbihkZXZfdHJhaW4pKSU+JQogIGZpbHRlcihpZD09bWluKGlkKSklPiUKICBwdWxsKGlkKQpiZXRhczwtaXRlcmFjaW9uZXNbaWRtaW4sXQojIGJldGFzCiNwO0s7CiMocCsxKSooSy0xKTsKI2xlbmd0aChiZXRhcykKCgpkZl9iZXRhcyA8LSBhc19kYXRhX2ZyYW1lKG1hdHJpeChiZXRhcywgSyAtIDEsIHAgKyAxICwgYnlyb3cgPSBUUlVFKSklPiUKICBiaW5kX3Jvd3MoCiAgICBhc19kYXRhX2ZyYW1lKG1hdHJpeChjKDEscmVwKDAscCkpLG5yb3c9MSkpCiAgICApJT4lCiAgbXV0YXRlKAogICAgZHg9YXMuY2hhcmFjdGVyKHJvd19udW1iZXIoKSkKICApJT4lCiAgbGVmdF9qb2luKAogICAgVVNJRE5FVF9yZWR1Y2lkYV8wNSU+JQogICAgICBncm91cF9ieShkeCxDYXRlZ29yeSklPiUKICAgICAgc3VtbWFyaXNlKAogICAgICAgIG49bigpCiAgICAgICklPiUKICAgICAgdW5ncm91cCgpJT4lCiAgICAgIG11dGF0ZSgKICAgICAgICBkeD1hcy5jaGFyYWN0ZXIoZHgpLAogICAgICAgIHByb3A9cm91bmQobi9zdW0obiksMykKICAgICAgKQogICklPiUKICBzZWxlY3QoZHgsQ2F0ZWdvcnksbixwcm9wLG9uZV9vZihuYW1lcyguKSkpCiAgCm5hbWVzKGRmX2JldGFzKTwtYygiZHgiLCJDYXRlZ29yeSIsIm4iLCJwcm9wIixwYXN0ZTAoImJldGFfIiwoc2VxKHArMSktMSkpKQpkZl9iZXRhcwojIHByb2RfbWF0cmljZXM8LWFzLm1hdHJpeChjYmluZCgxLCB4KSkgJSolIHQoYmV0YV9tYXQpCmBgYAoKIyMgKipDYWxjdWxhdGluZyB3ZWlnaHRzIGZvciBmZWF0dXJlIHBlciBEeCoqCmBgYHtyfQpleGNsdWlyPC1jKCJkeCIsIkNhdGVnb3J5IiwibiIsInByb3AiKQogICAgcHRzaWcwMDwtYXMuZGF0YS5mcmFtZSgKICAgICAgcG9iMDBfdHJhaW4wJT4lIAogICAgICAgICMgc2VsZWN0KCAtaWRfcHgsIC1DYXRlZ29yeSwgLWR4KSU+JQogICAgICAgIHNlbGVjdCggLWlkX3B4LCAtQ2F0ZWdvcnkpJT4lCiAgICAgICAgbXV0YXRlKGludGVyY2VwdD0xKSU+JQogICAgICAgIHNlbGVjdChkeCxpbnRlcmNlcHQsIG9uZV9vZihuYW1lcyguKSkpCiAgKSNbYygibWFsb3MiLCJkZW5vbXN5IixuYW1lcyhzaWVzdGFuKVshbmFtZXMoc2llc3RhbiklaW4lZXhjbHVpcl0pXQoKbGlzdGFfcmVzdWx0YWRvczwtbGlzdCgpCnRibF9wZXNvczwtZGF0YV9mcmFtZSgpCmZvcihjbGFzZV9pIGluIDE6Syl7CiAgIyBjbGFzZV9pPC0xCiAgcHJpbnQoY2xhc2VfaSkKICBkeF90bXA8LXBvYjAwX3RyYWluMCU+JWZpbHRlcihkeD09Y2xhc2VfaSklPiVkaXN0aW5jdChDYXRlZ29yeSklPiVwdWxsKENhdGVnb3J5KQogIG5fdG1wPC1kZl9iZXRhcyU+JWZpbHRlcihkeD09Y2xhc2VfaSklPiVkaXN0aW5jdChuKSU+JXB1bGwobikKICBwcm9wX3RtcDwtZGZfYmV0YXMlPiVmaWx0ZXIoZHg9PWNsYXNlX2kpJT4lZGlzdGluY3QocHJvcCklPiVwdWxsKHByb3ApCiAgcHJpbnQoZHhfdG1wKQogIHNpZXN0YW48LWFzLmRhdGEuZnJhbWUoZGZfYmV0YXMpW2NsYXNlX2ksXQogICMgcHRzaWcwMDwtcG9iX3Rlc3RbYygibWFsb3MiLCJkZW5vbXN5IixuYW1lcyhzaWVzdGFuKVshbmFtZXMoc2llc3RhbiklaW4lZXhjbHVpcl0pXQogICMgICBwdHNpZzwtYXMuZGF0YS5mcmFtZShhcHBseShwdHNpZzAwLDIsYXMubnVtZXJpYykpCiAgICBwdHNpZzwtYXMuZGF0YS5mcmFtZShhcHBseShwdHNpZzAwLDIsYXMubnVtZXJpYykpCiAgICB2YXJUaTwtMAogICAgIyBkYXRhX3ZhcnM8LWFzLmRhdGEuZnJhbWUocHRzaWdbMSxuYW1lcyhzaWVzdGFuKVshbmFtZXMoc2llc3RhbiklaW4lZXhjbHVpcl1bLTFdXSkKICAgIGRhdGFfdmFyczwtYXMuZGF0YS5mcmFtZShwdHNpZ1sxLG5hbWVzKHB0c2lnMDApWy1jKDE6MildXSkKICAgIG5hbWVzKGRhdGFfdmFycyk8LW5hbWVzKHB0c2lnMDApWy1jKDE6MildCiAgICBkYXRhX3ZhcnNbMSxdPC0wCiAgICBkYXRhX3ZhcnNbMixdPC0wCiAgICAjIGRhdGFfdmFyc1szLF08LTAKICAgIAogICAgZm9yKGogaW4gMToobGVuZ3RoKG5hbWVzKHB0c2lnKSktMikpewogICAgICAjIGo8LTEKICAgICAgI3ByaW50KG5hbWVzKHB0c2lnKVtqKzJdKQogICAgICAjcHJpbnQobmFtZXMoc2llc3RhblshbmFtZXMoc2llc3RhbiklaW4lZXhjbHVpcl1bMStqXSkpCiAgICAgIGJldGF3b2U8LXB0c2lnWywyK2pdKmFzLm51bWVyaWMoc2llc3RhblshbmFtZXMoc2llc3RhbiklaW4lZXhjbHVpcl1bMStqXSkKICAgICAgcHRzaWc8LWNiaW5kKHB0c2lnLGJldGF3b2UpCiAgICAgIAogICAgICB2YXJqPC1yb3VuZChzZChwdHNpZyRiZXRhd29lKSw0KQogICAgICAKICAgICAgIyB3b2VfdmFyPC1uYW1lcyhzaWVzdGFuKVshbmFtZXMoc2llc3RhbiklaW4lZXhjbHVpcl1baisxXQogICAgICAjIHZhcl9vcmlnaW5hbDwtZ3N1Yigid29lXyIsIiIsd29lX3ZhcikKICAgICAgIyBtdHIzX3RtcDwtTVRSM195ayhwdHNpZ1ssMytqXSxwdHNpZyRtYWxvcyxwdHNpZyRkZW5vbXN5KQogICAgICAKICAgICAgIyBnaW5pPC1hcy5kYXRhLmZyYW1lKAogICAgICAjICAgZGZfZ2luaXMlPiUKICAgICAgIyAgICAgZmlsdGVyKHZhcj09d29lX3ZhcikKICAgICAgIyApJEdpbmkgI3VuaXF1ZShsbXRyNSRsaXN0YSR3b2VzX25lc3AkR2luaVtsbXRyNSRsaXN0YSR3b2VzX25lc3AkdmFyPT12YXJfdG1wXSkKICAgICAgIyBtdHI0X3RtcDwtTVRSNF95ayhwdHNpZywid29lX0lNX01FRElPX1BBR09fVERDXzZNIix2b2JqID0gIm1hbG9zIixkZW5vbXN5ID0gcHRzaWckZGVub21zeSkKICAgICAgCiAgICAgIGRhdGFfdmFyc1sxLG5hbWVzKHB0c2lnKVtqKzJdXTwtbmFtZXMoc2llc3RhblshbmFtZXMoc2llc3RhbiklaW4lZXhjbHVpcl1bMStqXSkKICAgICAgZGF0YV92YXJzWzIsbmFtZXMocHRzaWcpW2orMl1dPC12YXJqCiAgICAgICMgZGF0YV92YXJzWzMsbmFtZXMocHRzaWcpW2orMl1dPC1uYW1lcyhwdHNpZylbaisyXQogICAgICAjIGRhdGFfdmFyc1szLG5hbWVzKHNpZXN0YW4pWyFuYW1lcyhzaWVzdGFuKSVpbiVleGNsdWlyXVsxK2pdXTwtZ2luaSAjbWF4KG10cjNfdG1wJGdhLG10cjNfdG1wJGdkKQogICAgICAKICAgICAgdmFyVGk8LXZhclRpICsgdmFyagogICAgICAKICAgICAgbmFtZXMocHRzaWcpW25jb2wocHRzaWcpXTwtcGFzdGUoImJldGEiLGdzdWIoIiAiLCJfIixuYW1lcyhwdHNpZylbMitqXSksc2VwPSJfIikKICAgICAgCiAgICB9CiAgICBkYXRhX3ZhcnMkdmFyVGk8LXZhclRpCiAgICAjIGRhdGFfdmFycyR2YXJUaVszXTwtMAogICAgcGVzb3M8LXJvdW5kKGFzLm51bWVyaWMoZGF0YV92YXJzWzIsXSkvZGF0YV92YXJzJHZhclRpWzJdLDQpCiAgICBkYXRhX3ZhcnM8LXJiaW5kKGRhdGFfdmFycyxwZXNvcykKICAgIHJvdy5uYW1lcyhkYXRhX3ZhcnMpPC1jKCJpZF9iZXRhIiwiZGVzdmlhY2lvbl9zIiwicGVzb3MiKSMibm9tYnJlX29yaWdpbmFsIiwicGVzb3MiKQogICAgI3ByaW50KGRhdGFfdmFycykKICAgIGRhdGFfdmFyczwtYXMuZGF0YS5mcmFtZSh0KGRhdGFfdmFycykpCiAgICAjcHJpbnQoZGF0YV92YXJzKQogICAgCiAgICAjcHJpbnQobGVuZ3RoKHQoc2llc3RhblshbmFtZXMoc2llc3RhbiklaW4lZXhjbHVpcl1bLTFdKSkpCiAgICAjYmxhYmxhPC10KHNpZXN0YW5bIW5hbWVzKHNpZXN0YW4pJWluJWV4Y2x1aXJdWy0xXSkKICAgICNwcmludChibGFibGEpCiAgICBkYXRhX3ZhcnMkYmV0YV92YWx1ZSA8LSBjKHQoc2llc3RhblshbmFtZXMoc2llc3RhbiklaW4lZXhjbHVpcl1bLTFdKSwtOTk5OSkKICAgIGRhdGFfdmFycyRjbGFzZV9pPC1jbGFzZV9pCiAgICBkYXRhX3ZhcnMkRGlhZ25vc3RpY288LWR4X3RtcAogICAgZGF0YV92YXJzJG51bV9jYXNvc19keDwtbl90bXAKICAgIGRhdGFfdmFycyRwcm9wb3JjaW9uX2Nhc29zX2R4PC1wcm9wX3RtcAogICAgZGF0YV92YXJzJHZhcmlhYmxlPC1yb3cubmFtZXMoZGF0YV92YXJzKQogICAgCiAgICAjIGRhdGFfdmFyczwtZGF0YV92YXJzWyxjKDYsMTo1KV0KICAgIGRhdGFfdmFyczwtZGF0YV92YXJzJT4lCiAgICAgIHNlbGVjdChEaWFnbm9zdGljbyxjbGFzZV9pLG51bV9jYXNvc19keCxwcm9wb3JjaW9uX2Nhc29zX2R4LHZhcmlhYmxlLGlkX2JldGEsYmV0YV92YWx1ZSxkZXN2aWFjaW9uX3MscGVzb3MpCiAgICAKICAgIAogICAgdGJsX3Blc29zPC1yYmluZCh0YmxfcGVzb3MsZGF0YV92YXJzKQp9Cgp0YmxfcGVzb3M8LXRibF9wZXNvcyU+JQogIG11dGF0ZSgKICAgIHZhcmlhYmxlPWdzdWIoJ0Jham8nLCdsb3cnLHZhcmlhYmxlKSwKICAgIHZhcmlhYmxlPWdzdWIoJ0FsdG8nLCdoaWdoJyx2YXJpYWJsZSksCiAgICB2YXJpYWJsZT1nc3ViKCdMSU5GT0NJVE9TJywnTHltcGhvY3l0ZXMnLHZhcmlhYmxlKSwKICAgIHZhcmlhYmxlPWdzdWIoJ0xFVUNPQ0lUT1MnLCdMZXVrb2N5dGVzJyx2YXJpYWJsZSksCiAgICB2YXJpYWJsZT1nc3ViKCdNT05PQ0lUT1MnLCdNb25vY3l0ZXMnLHZhcmlhYmxlKSwKICAgIHZhcmlhYmxlPWdzdWIoJ05FVVRST0ZJTE9TJywnTmV1dHJvcGhpbHMnLHZhcmlhYmxlKSwKICAgIHZhcmlhYmxlPWdzdWIoJ1BMQVFVRVRBUycsJ1BsYXRlbGV0ZXMnLHZhcmlhYmxlKSwKICAgIHZhcmlhYmxlPWdzdWIoJ0VPU0lOT0ZJTE9TJywnRW9zaW5vcGhpbHMnLHZhcmlhYmxlKSwKICAgIHZhcmlhYmxlPWdzdWIoJ0RvbG9yX2FiZG9taW5hbCcsJ0lfQWJkb21pbmFsX3BhaW4nLHZhcmlhYmxlKSwKICAgIHZhcmlhYmxlPWdzdWIoJ1JlZmx1am9fZ2FzdHJvZXNvZmFnaWNvJywnSV9HYXN0cm9lc29waGFnZWFsX3JlZmx1eCcsdmFyaWFibGUpLAogICAgdmFyaWFibGU9Z3N1YignRG9sb3JfdG9yYWNpY28nLCdJX1Rob3JhY2ljX3BhaW4nLHZhcmlhYmxlKSwKICAgIHZhcmlhYmxlPWdzdWIoJ0RvbG9yJywnSV9QYWluJyx2YXJpYWJsZSksCiAgICB2YXJpYWJsZT1nc3ViKCdBbHRvJywnaGlnaCcsdmFyaWFibGUpCiAgKQp0YmxfcGVzb3MlPiUKICBzZWxlY3QoRGlhZ25vc3RpY28sbnVtX2Nhc29zX2R4LHByb3BvcmNpb25fY2Fzb3NfZHgsdmFyaWFibGUsYmV0YV92YWx1ZSxwZXNvcyklPiUKICByZW5hbWUoRHggPSBEaWFnbm9zdGljbyklPiUKICByZW5hbWUobnVtYmVyX2Nhc2VzX3Blcl9keCA9IG51bV9jYXNvc19keCklPiUKICByZW5hbWUocGVyY2VudGFnZV9jYXNlc19wZXJfZHggPSBwcm9wb3JjaW9uX2Nhc29zX2R4KSU+JQogIHJlbmFtZShmZWF0dXJlID0gdmFyaWFibGUpJT4lCiAgcmVuYW1lKHdlaWdodF9pbXBvcnRhbmNlID0gcGVzb3MpCmBgYAoKCiMjICoqVG9wIDUgbW9yZSBpbXBvcnRhbnQgZmVhdHVyZXMgcGVyIERYKioKCmBgYHtyfQp0YmxfcGVzb3NfMDA8LXRibF9wZXNvcyU+JQogIG11dGF0ZSgKICAgIGRlc3ZpYWNpb25fcz1hcy5udW1lcmljKGRlc3ZpYWNpb25fcyksCiAgICBwZXNvcz1hcy5udW1lcmljKHBlc29zKQogICklPiUKICAjIGZpbHRlcih2YXJpYWJsZSE9InZhclRpIiklPiUKICBhcnJhbmdlKGRlc2MocHJvcG9yY2lvbl9jYXNvc19keCksRGlhZ25vc3RpY28sZGVzYyhwZXNvcykpCnNhdmVSRFModGJsX3Blc29zXzAwLCJkYXRhL3RibF9wZXNvc18wMF91c2lkbmV0NGFfaXRlcmFjaW9uZXNfNTAwMV8wc18wLjAwMDFfY29weS5yZHMiKQoKdGJsX3Blc29zXzAwPC1yZWFkUkRTKCJkYXRhL3RibF9wZXNvc18wMF91c2lkbmV0NGFfaXRlcmFjaW9uZXNfNTAwMV8wc18wLjAwMDFfY29weS5yZHMiKQoKCnRibF9wZXNvc18wMTwtdGJsX3Blc29zXzAwJT4lCiAgZmlsdGVyKHZhcmlhYmxlIT0idmFyVGkiKSU+JQogIGdyb3VwX2J5KERpYWdub3N0aWNvKSU+JQogIGFycmFuZ2UoRGlhZ25vc3RpY28sZGVzYyhwZXNvcykpJT4lCiAgbXV0YXRlKAogICAgbml2ZWxfcGVzb3M9cm93X251bWJlcigpCiAgKSU+JQogIHVuZ3JvdXAoKSU+JQogIGFycmFuZ2UoZGVzYyhwcm9wb3JjaW9uX2Nhc29zX2R4KSxEaWFnbm9zdGljbyxkZXNjKHBlc29zKSkKCnNhdmVSRFModGJsX3Blc29zXzAxLCJkYXRhL3RibF9wZXNvc18wMV91c2lkbmV0NGFfaXRlcmFjaW9uZXNfNTAwMV8wc18wLjAwMDFfY29weS5yZHMiKQp3cml0ZS5jc3YodGJsX3Blc29zXzAxLCJkYXRhL3RibF9wZXNvc18wMV91c2lkbmV0NGFfaXRlcmFjaW9uZXNfNTAwMV8wc18wLjAwMDFfY29weS5jc3YiLHJvdy5uYW1lcyA9IEYpCgoKdGJsX3Blc29zXzAxJT4lCiAgZ3JvdXBfYnkoRGlhZ25vc3RpY28pJT4lCiAgYXJyYW5nZShEaWFnbm9zdGljbyxkZXNjKHBlc29zKSklPiUKICBtdXRhdGUoCiAgICBuaXZlbF9wZXNvcz1yb3dfbnVtYmVyKCkKICApJT4lCiAgZmlsdGVyKG5pdmVsX3Blc29zPD01KSU+JQogIHVuZ3JvdXAoKSU+JQogIGFycmFuZ2UoZGVzYyhwcm9wb3JjaW9uX2Nhc29zX2R4KSxEaWFnbm9zdGljbyxkZXNjKHBlc29zKSklPiUKICBzZWxlY3QoRGlhZ25vc3RpY28sdmFyaWFibGUsYmV0YV92YWx1ZSxwZXNvcyklPiUKICByZW5hbWUoRHggPSBEaWFnbm9zdGljbyklPiUKICByZW5hbWUoZmVhdHVyZSA9IHZhcmlhYmxlKSU+JQogIHJlbmFtZSh3ZWlnaHRfaW1wb3J0YW5jZSA9IHBlc29zKQpgYGAKCiMgRmluYWwgQ29tbWVudHMKCiogU2luY2UgdGhlIGJlc3QgbW9kZWwncyBwZXJmb3JtYW5jZSBpcyB0b28gbG93ICh+YDU0JSBpbiB0ZXN0IHNhbXBsZWApLCAqKml0J3MgYmVlbiBkZWNpZGVkIHRvIHRyeSB3aXRoIGFub3RoZXIgTWFjaGluZSBMZWFybmluZyBUZWNobmlxdWUgc3VjaCBhcyBgWEdCb29zdGAqKgo=